#ifndef _lib_win32_window_h_
#define _lib_win32_window_h_

#include "win32/lean_windows.h"

#include <map>

struct Window
{
private:
	static bool s_window_class_created;
	static std::map<HWND, Window*> s_tracked_windows;
	static const char* s_class_name;
	static LRESULT CALLBACK wnd_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
	static int s_timer_id_counter;
	
	HINSTANCE m_hinstance;
	HWND m_hwnd;
	
	int m_x;
	int m_y;
	int m_width;
	int m_height;

	int m_timer_interval;
	int m_timer_id;
	
	HFONT m_font;
	int m_font_width;
	int m_font_height;
	
	COLORREF m_bg_color;
	COLORREF m_fg_color;
	
	// Call this first.
	static void create_window_class(HINSTANCE hinstance);
public:

	// This is the main loop.
	static int message_loop();

	// Constructor.
	// timer_interval is the amount of time in milliseconds between timer events.
	// Specify 0 if you don't want them.
	Window(
		HINSTANCE hinstance,
		int width,
		int height,
		const char* title,
		int timer_interval);
	
	// Show the window. For C++-technical reasons this cannot be done in
	// the constructor. (We need to trigger a WM_PAINT event inside the
	// constructor but the virtual table for the derived class is not
	// initialized at that time.)
	void show();
	
	// Hide the window.
	void hide();
	
	// Destructor.
	virtual ~Window()
	{
		s_tracked_windows.erase(m_hwnd);

		if (m_timer_id != -1)
			KillTimer(m_hwnd, m_timer_id);
	}
	
	/// Getters & setters

	HWND hwnd()
	{
		return m_hwnd;
	}
	
	int width()
	{
		return m_width;
	}
	
	int height()
	{
		return m_height;
	}
	
	HFONT font()
	{
		return m_font;
	}
	
	int font_width()
	{
		return m_font_width;
	}
	
	int font_height()
	{
		return m_font_height;
	}
	
	void resize(int width, int height);
	
	COLORREF bg_color()
	{
		return m_bg_color;
	}
	
	void bg_color(COLORREF value)
	{
		m_bg_color = value;
	}
	
	COLORREF fg_color()
	{
		return m_fg_color;
	}
	
	void fg_color(COLORREF value)
	{
		m_fg_color = value;
	}
	
	enum Message_Types
	{
		mt_timer =		0x0001, // Timer messages
		mt_paint =		0x0002,	// All paint messages
		mt_mouse =		0x0004, // All mouse messages
		mt_keyboard =	0x0008, // All keyboard messages
		mt_resize =		0x0010, // All resize messages
		
		mt_all =		0xffff // All messages
	};
	
	/// Virtual functions start here.

	// Which message types am I interested in?
	virtual Message_Types my_message_types()
	{
		return mt_all;
	}
	
	// Callback functions meant to be overridden

	// Timer event. Will be called each m_timer
	virtual void on_timer()
	{
	}
		
	// Paint.
	// ps is the structure returned by BeginPaint(), and EndPaint()
	// is called automatically. Font is set as well. fg and bg are a
	// pen and a brush, accordingly, set to colors fg_color()
	// and bg_color(). Default implementation clears the background.
	virtual void on_paint(const PAINTSTRUCT& ps, HPEN fg, HBRUSH bg)
	{
		RECT client_area;
		SetRect(&client_area, 0, 0, width(), height());
		FillRect(ps.hdc, &client_area, bg);
	}

	enum MouseButton
	{
		mb_left,
		mb_mid,
		mb_right
	};
	
	virtual void on_mouse_down(MouseButton button, int x, int y)
	{
	}
	
	virtual void on_mouse_up(MouseButton button, int x, int y)
	{
	}

	virtual void on_mouse_doubleclick(MouseButton button, int x, int y)
	{
	}
		
	virtual void on_mouse_move(int x, int y)
	{	
	}

	virtual void on_key_down(int keycode, bool was_repeated)
	{
	}

	virtual void on_key_char(int char_code, bool was_repeated)
	{
	}

	virtual void on_key_up(int keycode)
	{
	}
	
	/// Resize operations
	virtual void on_resize_starting()
	{
	}

	virtual void on_resize_finished()
	{
	}

	// Resize. Updates width and height. You should call this if you
	// override.
	virtual void on_resize(int width, int height)
	{
		m_width = width;
		m_height = height;
	}
	
	// Resizing. Changes to width and height will be propagated to the
	// drag rectangle.
	virtual void on_resizing(int& width, int& height)
	{
	}
};

#endif